
#ifndef __IWL_CONTROLLER_MONITOR_H__
#define __IWL_CONTROLLER_MONITOR_H__

#include "RtDebug.h"
#include "RtTimerWrapper.h"
#include "RtTimeValue.h"
#include "RtConfigInitFile.h"
#include "RtThreadInterface.h"

#define IWL_CFG_GROUP_MONITOR "Monitor"
#define IWL_CFG_ITEM_MONITORTYPE "MonitorType"
#define IWL_CFG_ITEM_MONITORINTERVAL "MonitorInterval"
#define IWL_CFG_ITEM_MONITORFILESIZE "MonitorFileSize"

#define SIZE_MB 1024*1024
#define GC_MONITOR_END_STRING " << END >>\n\n"

#ifdef RT_WIN32

#include <direct.h>

#else // RT_WIN32

#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>

#endif// RT_WIN32

template <class TargetType>
class CMonitorT: public CRtTimerWrapperSink
{
public:
	enum MonitorType
	{
		MonitorType_NONE = 0 ,
		MonitorType_SCREAN = (1 << 0),
		MonitorType_FILE = (1 << 1),
		MonitorType_ALL = ((1 << 2) - 1)
	};

	CMonitorT(TargetType* pTarget)
	: m_pTarget(pTarget)
	, m_nType(MonitorType_NONE)
	, m_nInterval(5)
	, m_dwFileSize(1)
	, m_pLogFile(NULL)
	{
	}
	virtual ~CMonitorT()
	{
		Release();
	}

public:
	RtResult Initialize(const char* szModuleName = NULL)
	{
		/* Get GcMonitor configuration */
		CRtConfigInitFile configInit;
		m_nType = configInit.GetWordParam(IWL_CFG_GROUP_MONITOR, IWL_CFG_ITEM_MONITORTYPE, MonitorType_NONE);
		if (m_nType>MonitorType_ALL) 
			m_nType = MonitorType_NONE;
		m_nInterval = configInit.GetWordParam(IWL_CFG_GROUP_MONITOR, IWL_CFG_ITEM_MONITORINTERVAL, 5);
		m_dwFileSize = configInit.GetDwordParam(IWL_CFG_GROUP_MONITOR, IWL_CFG_ITEM_MONITORFILESIZE, 20);

		if (RT_BIT_ENABLED(m_nType, MonitorType_FILE)) {
			const char* szGcMonitorLogPath = "MonitorLog";
#ifdef RT_WIN32
			int rv = mkdir(szGcMonitorLogPath);
#else
			int rv = mkdir(szGcMonitorLogPath, S_IRWXU);
#endif
			if (rv!=0 && errno!=EEXIST) {
				RT_ERROR_TRACE_THIS("CMonitorT::Initialize, create log path failed! errno="<<errno);
				return RT_ERROR_FAILURE;
			}
			char szLogFileName[256] = { 0x00 };
			snprintf(szLogFileName, 255, "%s/%s.%d.log", szGcMonitorLogPath, szModuleName?szModuleName:CRtTrace::Instance()->GetProcessName(), (int)getpid());
			m_pLogFile = fopen(szLogFileName, "w+");
			if ( m_pLogFile == NULL ) {
				RT_ERROR_TRACE("CMonitorT::Initialize, Open Monitor Log file Error! errno="<<errno);
				return RT_ERROR_FAILURE;
			}
		}

		if (m_nType>MonitorType_NONE && m_nType<=MonitorType_ALL)
			m_monitorTimer.Schedule(this, (time_t)m_nInterval, 0);
		m_strMessage.reserve(1024*8);
		return RT_OK;
	}

	void Release()
	{
		m_pTarget = NULL;
		m_monitorTimer.Cancel();
		if ( m_pLogFile != NULL )
			fclose(m_pLogFile);
		m_pLogFile = NULL;
	}

	static CRtString GetMonitorHeader()
	{
		CRtTimeValue tvCur = CRtTimeValue::GetTimeOfDay();
		time_t timeSec = tvCur.GetSec();
		struct tm tmVar;
#if defined (RT_WIN32)
		tmVar = *(localtime(&timeSec));
#else 
		localtime_r(&timeSec, &tmVar);
#endif
		char szBuf[128] = { 0x00 };
		/* monitor header */
		snprintf(szBuf , 127, "\n[ %4d-%02d-%02d %02d:%02d:%02d.%03d ]\n", 
			tmVar.tm_year + 1900, tmVar.tm_mon + 1, tmVar.tm_mday, tmVar.tm_hour, 
			tmVar.tm_min, tmVar.tm_sec, tvCur.GetUsec()/1000);
		return CRtString(szBuf);
	}

protected: // from CRtTimerWrapperSink
	virtual void OnTimer(CRtTimerWrapper* aId)
	{
		m_strMessage = CMonitorT<TargetType>::GetMonitorHeader();
		if (m_pTarget) 
		{
			m_pTarget->GenerateMonitorInfo(m_strMessage);
		}
		OutputMsg(m_strMessage);
	}

protected:
	void OutputMsg(const CRtString& strMsg)
	{
		if (strMsg.empty())
			return;

		if (RT_BIT_ENABLED(m_nType, MonitorType_SCREAN))
		{
			printf("%s", strMsg.c_str());
		}

		if (RT_BIT_ENABLED(m_nType, MonitorType_FILE))
		{
			unsigned long curPos = ftell(m_pLogFile);
			if (curPos>0) {
#ifdef RT_UNIX
				fseek(m_pLogFile, 0-strlen(GC_MONITOR_END_STRING), SEEK_CUR);
#else
				fseek(m_pLogFile, 0-strlen(GC_MONITOR_END_STRING)-2, SEEK_CUR);
#endif
			}
			fprintf(m_pLogFile, strMsg.c_str());
//			fflush(m_pLogFile);
			if ( curPos > m_dwFileSize*SIZE_MB)
			{
				fprintf(m_pLogFile, "<====\n");
				fseek(m_pLogFile, 0, SEEK_SET);
			}
			fprintf(m_pLogFile, GC_MONITOR_END_STRING);
			fflush(m_pLogFile);
		}
	}

protected:
	TargetType* m_pTarget;
	CRtTimerWrapper m_monitorTimer;
	
	WORD m_nType;
	WORD m_nInterval; // unit: second
	DWORD m_dwFileSize; // unit: MB
	FILE*  m_pLogFile;
	CRtString m_strMessage;
};

template<typename TargetObjectType>
class ThreadMonitor;

template <typename TargetObjectType>
class MonitorResultEvent : public IRtEvent
{
public:
	MonitorResultEvent(ThreadMonitor<TargetObjectType>* pMonitor)
		:m_pMonitor(pMonitor)
	{
	}

	virtual ~MonitorResultEvent()
	{
	}

	virtual RtResult OnEventFire()
	{
		m_pMonitor->OnMonitorResult();
		return RT_OK;
	}

private:
	ThreadMonitor<TargetObjectType>* m_pMonitor;
};


template<typename TargetObjectType>
class MonitorEvent : public IRtEvent
{
public:
	MonitorEvent(RT_THREAD_ID MonitorThreadID ,
				 ThreadMonitor<TargetObjectType>* pMonitor , 
				 TargetObjectType* pTarget)
		:m_MonitorThreadID(MonitorThreadID) ,
		 m_pMonitor(pMonitor), 
		 m_pTarget(pTarget)
	{}
	virtual ~MonitorEvent() {}
	// interface IRtEvent
	virtual RtResult OnEventFire()
	{
		m_pTarget->GenerateMonitorInfo(m_pMonitor->GetMonitorStringRef());
		MonitorResultEvent<TargetObjectType>* pEvent = new MonitorResultEvent<TargetObjectType>(m_pMonitor);
		CRtThread* pThread = CRtThreadManager::Instance()->GetThread(m_MonitorThreadID);
		if ( pThread != NULL )
		{
			pThread->GetEventQueue()->PostEvent(pEvent);
		}
		return RT_OK;
	}
private:
	RT_THREAD_ID m_MonitorThreadID;
	ThreadMonitor<TargetObjectType>* m_pMonitor;
	TargetObjectType* m_pTarget;
};

template <typename TargetObjectType>
class ThreadMonitor : public CMonitorT<TargetObjectType>
{
public:
	ThreadMonitor(RT_THREAD_ID TragetThreadID ,TargetObjectType* TargetObject)
		:CMonitorT<TargetObjectType>(TargetObject) ,
		m_TargetThreadID(TragetThreadID),
		m_bBusy(FALSE)
	{}
	virtual ~ThreadMonitor(){}

	virtual void OnTimer(CRtTimerWrapper* aId)
	{
		if ( m_bBusy )
			return;

		this->m_strMessage = CMonitorT<TargetObjectType>::GetMonitorHeader();

		MonitorEvent<TargetObjectType>* pEvent = new MonitorEvent<TargetObjectType>( CRtThreadManager::TT_MAIN , this , this->m_pTarget );
		CRtThread* pThread = CRtThreadManager::Instance()->GetThread(m_TargetThreadID);
		if ( pThread != NULL )
		{
			pThread->GetEventQueue()->PostEvent(pEvent);
			m_bBusy = TRUE;
		}else
			delete pEvent;
	}

	void OnMonitorResult()
	{
		OutputMsg(this->m_strMessage);
		m_bBusy = FALSE;
	}

	CRtString& GetMonitorStringRef()
	{
		return this->m_strMessage;
	}

private:
	RT_THREAD_ID m_TargetThreadID;
	BOOL m_bBusy;
};

#endif // __IWL_CONTROLLER_MONITOR_H__

